package com.maimang.platform.activiti.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.maimang.platform.activiti.dto.ProcessDefinitionDto;
import com.maimang.platform.activiti.service.ActProcessService;
import com.maimang.platform.base.Result;
import com.maimang.platform.enums.Status;
import com.maimang.platform.lang.ConvertUtils;
import com.maimang.platform.lang.StringUtils;
import com.maimang.platform.page.TableDataInfo;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;

/**
 * 流程管理
 *
 * @author Rocky
 * @create 2018-12-12
 */
@Service
public class ActProcessServiceImpl implements ActProcessService {
	@Autowired
	RepositoryService repositoryService;

	@Autowired
	RuntimeService runtimeService;

	@Autowired
	protected ObjectMapper objectMapper;

	/**
	 * 将流程定义转换成模型
	 *
	 * @param processId 流程编号
	 * @return 模型数据
	 * @throws Exception
	 */
	@Override
	public Model convertToModel(String processId) throws Exception {
		ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
				.processDefinitionId(processId).singleResult();
		InputStream bpmnStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
				processDefinition.getResourceName());
		XMLInputFactory xif = XMLInputFactory.newInstance();
		InputStreamReader in = new InputStreamReader(bpmnStream, StandardCharsets.UTF_8);
		XMLStreamReader xtr = xif.createXMLStreamReader(in);
		BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

		BpmnJsonConverter converter = new BpmnJsonConverter();
		ObjectNode modelNode = converter.convertToJson(bpmnModel);
		Model modelData = repositoryService.newModel();
		modelData.setKey(processDefinition.getKey());
		modelData.setName(processDefinition.getResourceName());
		modelData.setCategory(processDefinition.getCategory());
		modelData.setDeploymentId(processDefinition.getDeploymentId());
		modelData.setVersion(Integer.parseInt(
				String.valueOf(repositoryService.createModelQuery().modelKey(modelData.getKey()).count() + 1)));

		ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
		modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
		modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, modelData.getVersion());
		modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
		modelData.setMetaInfo(modelObjectNode.toString());

		repositoryService.saveModel(modelData);
		repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes(StandardCharsets.UTF_8));
		return modelData;
	}

	/**
	 * 使用部署对象ID查看流程图
	 *
	 * @param deploymentId 部署id
	 * @param imageName    资源文件名
	 * @return 文件流
	 */
	@Override
	public InputStream findImageStream(String deploymentId, String imageName) throws Exception {
		return repositoryService.getResourceAsStream(deploymentId, imageName);
	}

	/**
	 * 查询流程定义
	 *
	 * @param processDefinition 流程信息
	 * @return 流程集合
	 */
	@Override
	public TableDataInfo selectProcessDefinitionList(ProcessDefinitionDto processDefinition) {
		TableDataInfo data = new TableDataInfo();
		ProcessDefinitionQuery pdQuery = repositoryService.createProcessDefinitionQuery();
		if (StringUtils.isNotEmpty(processDefinition.getKey())) {
			pdQuery.processDefinitionKey(processDefinition.getKey());
		}
		if (StringUtils.isNotEmpty(processDefinition.getName())) {
			pdQuery.processDefinitionName(processDefinition.getName());
		}
		if (StringUtils.isNotEmpty(processDefinition.getDeploymentId())) {
			pdQuery.deploymentId(processDefinition.getDeploymentId());
		}
		data.setCode(Status.success.code);
		data.setTotal(pdQuery.count());
		data.setRows(pdQuery.orderByDeploymentId().desc()
				.listPage(processDefinition.getPageNum(), processDefinition.getPageSize()).stream()
				.map(ProcessDefinitionDto::new).collect(Collectors.toList()));
		return data;
	}

	/**
	 * 部署流程定义
	 *
	 * @param is       文件流
	 * @param fileName 文件名称
	 * @param category 类型
	 * @return 结果
	 */
	@Override
	public Result saveNameDeplove(InputStream is, String fileName, String category) {
		ZipInputStream zipInputStream = null;
		try {
			String extension = FilenameUtils.getExtension(fileName);
			Deployment deployment = null;
			String message = "";
			switch (extension) {
				case "zip":
				case "bar":
					zipInputStream = new ZipInputStream(is);
					// 创建流程定义
					deployment = repositoryService.createDeployment().name(fileName).addZipInputStream(zipInputStream).deploy();
					break;
				case "png":
				case "bpmn20.xml":
					deployment = repositoryService.createDeployment().addInputStream(fileName, is).deploy();
					break;
				case "bpmn":
					String baseName = FilenameUtils.getBaseName(fileName);
					deployment = repositoryService.createDeployment().addInputStream(baseName + ".bpmn20.xml", is).deploy();
					break;
				default:
					message = "不支持的文件类型：" + extension;
					break;
			}
			// 支持的文件上传
			if (StringUtils.isEmpty(message)) {
				List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
				// 设置流程分类
				for (ProcessDefinition processDefinition : list) {
					repositoryService.setProcessDefinitionCategory(processDefinition.getId(), category);
					message += StringUtils.format("部署成功，流程编号[{}]", processDefinition.getId()) + "<br/>";
				}
				if (list.size() == 0) {
					message = "部署失败，没有流程。";
				}
			}
			return Result.SUCCESS("200", message);
		} catch (Exception e) {
			return Result.FAIL("500", e.getMessage());
		}
	}

	/**
	 * 根据流程部署id，删除流程定义
	 *
	 * @param ids 部署ids
	 * @return 结果
	 */
	@Override
	public boolean deleteProcessDefinitionByDeploymentIds(String ids) {
		boolean result = true;
		try {
			String[] deploymentIds = ConvertUtils.toStrArray(ids);
			for (String deploymentId : deploymentIds) {
				// 级联删除，不管流程是否启动，都能可以删除
				repositoryService.deleteDeployment(deploymentId, true);
			}
		} catch (Exception e) {
			result = false;
			throw e;
		}
		return result;
	}
}
